/*
 *  Copyright 2009 Jamie Gennis <jgennis@gmail.com>.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package com.google.code.gwt.remoteaction.batch.client;

import com.google.code.gwt.remoteaction.client.AbstractActionProxy;
import com.google.code.gwt.remoteaction.client.Action;
import com.google.code.gwt.remoteaction.client.ActionExecutionService;
import com.google.code.gwt.remoteaction.client.ActionExecutionServiceAsync;
import com.google.code.gwt.remoteaction.client.ActionFilter;
import com.google.code.gwt.remoteaction.client.ActionProxy;
import com.google.code.gwt.remoteaction.client.ActionResult;
import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * GWT tests for the Remote Action Batch module.
 *
 * @author Jamie Gennis <jgennis@gmail.com>
 */
public class GwtTestRemoteActionBatch extends GWTTestCase {

  /**
   * An action filter for counting the number of actions that get executed.
   */
  static class ActionCounter extends AbstractActionProxy implements ActionFilter {

    protected int numActionsExecuted = 0;

    public void execute(Action action, AsyncCallback<ActionResult> callback) {
      numActionsExecuted++;
      proxyExecution(action, callback);
    }

    public int getNumActionsExecuted() {
      return numActionsExecuted;
    }
  }

  ActionExecutionServiceAsync actionExecutor;

  ServiceAsync service;

  ActionCounter actionCounter;

  int numResultsProcessed;

  @Override
  public String getModuleName() {
    return "com.google.code.gwt.remoteaction.batch.RemoteActionBatch";
  }

  @Override
  protected void gwtSetUp() throws Exception {
    actionExecutor = GWT.create(ActionExecutionService.class);
    service = GWT.create(Service.class);
    actionCounter = new ActionCounter();
    numResultsProcessed = 0;
  }

  public void testDeferredBatchingActionFilter() throws Exception {
    final int NUM_RESULTS_EXPECTED = 7;
    final int NUM_BATCHES_EXPECTED = 2;

    // service -> (batcher -> batchExecutor) -> actionCounter -> actionExecutor
    DeferredBatchingActionFilter batcher = new DeferredBatchingActionFilter();
    ((ActionProxy) service).setActionExecutor(batcher);
    ((ActionProxy) batcher).setActionExecutor(actionCounter);
    ((ActionProxy) actionCounter).setActionExecutor(actionExecutor);

    service.get2(2, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(2, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });
    service.get3(3, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(3, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });
    service.get5(5, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(5, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });
    service.get7(7, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(7, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });
    service.get11(11, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        assertTrue(caught instanceof Service.Exception11);
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }

      public void onSuccess(Integer result) {
        fail("expected exception Exception11");
      }
    });

    Timer timer = new Timer() {

      @Override
      public void run() {
        service.get7(7, new AsyncCallback<Integer>() {

          public void onFailure(Throwable caught) {
            throw new RuntimeException(caught);
          }

          public void onSuccess(Integer result) {
            assertEquals(7, result.intValue());
            if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
              assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
              finishTest();
            }
          }
        });
        service.get11(11, new AsyncCallback<Integer>() {

          public void onFailure(Throwable caught) {
            assertTrue(caught instanceof Service.Exception11);
            if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
              assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
              finishTest();
            }
          }

          public void onSuccess(Integer result) {
            fail("expected exception Exception11");
          }
        });
      }
    };
    timer.schedule(500);

    delayTestFinish(20000);
  }

  public void testDelayedBatchingActionFilter() throws Exception {
    final int NUM_RESULTS_EXPECTED = 7;
    final int NUM_BATCHES_EXPECTED = 2;

    // service -> (batcher -> batchExecutor) -> actionCounter -> actionExecutor
    DelayedBatchingActionFilter batcher = new DelayedBatchingActionFilter();
    ((ActionProxy) service).setActionExecutor(batcher);
    ((ActionProxy) batcher).setActionExecutor(actionCounter);
    ((ActionProxy) actionCounter).setActionExecutor(actionExecutor);
    batcher.setDelay(1000);

    service.get2(2, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(2, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });
    service.get3(3, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(3, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });
    service.get5(5, new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(5, result.intValue());
        if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
          assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
          finishTest();
        }
      }
    });

    Timer timer = new Timer() {

      @Override
      public void run() {
        service.get7(7, new AsyncCallback<Integer>() {

          public void onFailure(Throwable caught) {
            throw new RuntimeException(caught);
          }

          public void onSuccess(Integer result) {
            assertEquals(7, result.intValue());
            if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
              assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
              finishTest();
            }
          }
        });
        service.get11(11, new AsyncCallback<Integer>() {

          public void onFailure(Throwable caught) {
            assertTrue(caught instanceof Service.Exception11);
            if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
              assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
              finishTest();
            }
          }

          public void onSuccess(Integer result) {
            fail("expected exception Exception11");
          }
        });
      }
    };
    timer.schedule(200);

    timer = new Timer() {

      @Override
      public void run() {
        service.get7(7, new AsyncCallback<Integer>() {

          public void onFailure(Throwable caught) {
            throw new RuntimeException(caught);
          }

          public void onSuccess(Integer result) {
            assertEquals(7, result.intValue());
            if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
              assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
              finishTest();
            }
          }
        });
        service.get11(11, new AsyncCallback<Integer>() {

          public void onFailure(Throwable caught) {
            assertTrue(caught instanceof Service.Exception11);
            if (++numResultsProcessed == NUM_RESULTS_EXPECTED) {
              assertEquals(NUM_BATCHES_EXPECTED, actionCounter.getNumActionsExecuted());
              finishTest();
            }
          }

          public void onSuccess(Integer result) {
            fail("expected exception Exception11");
          }
        });
      }
    };
    timer.schedule(2000);

    delayTestFinish(20000);
  }
}
